package com.ltmonitor.jt808.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.ltmonitor.jt808.protocol.JT_8003;
import com.ltmonitor.jt808.protocol.T808Message;
import com.ltmonitor.jt808.protocol.T808MessageHeader;
import com.ltmonitor.jt808.service.IGpsDataService;
import com.ltmonitor.jt808.service.IMessageSender;
import com.ltmonitor.jt808.service.IMultiPacketMessageService;
import com.ltmonitor.util.DateUtil;

@Service("multiPacketMessageService")
public class MultiPacketMessageService implements IMultiPacketMessageService {

	private static Logger logger = Logger.getLogger(MediaService.class);

	/**
	 * 调用此类，对终端应答，是否需要重传，或者应答 成功
	 */
	private IMessageSender messageSender;
	
	/**
	 * 多长时间没有收到包，视为超时
	 */
	@Value("#{config.mediaUploadTimeOut}")  
	private int mediaUploadTimeOut = 15;
	/**
	 * 检测次数，如果超过此次数，包仍然不完整，则抛弃整个包
	 */
	@Value("#{config.maxRetransTimes}")  
	private int maxRetransTimes = 3;

	private boolean continueCheck = true;
	/**
	 * 检查分包完整性的线程
	 */
	private Thread checkPacketThread;

	/**
	 * 数据处理服务，当收到完整的包后，重新解析后交给gpsDataService处理
	 */
	@Autowired
	private IGpsDataService gpsDataService;
	
	/**
	 * 存储某一消息的所有分包,键值Key是车辆的SimNo，Value是某一消息包的所有分包 如果消息有分包，则当收到所有分包后，才能进行完整的解析
	 */
	public ConcurrentMap<String, MultiPacketMessage> multiPacketMessageMap = new ConcurrentHashMap<String, MultiPacketMessage>();
	/**
	 * 启动检测完整性的线程服务
	 */
	public void start()
	{
		checkPacketThread = new Thread(new Runnable() {
			public void run() {
				checkPacketThreadFunc();
			}
		});
		checkPacketThread.start();
	}
	

	public void Stop() {
		continueCheck = false;
		try {
			checkPacketThread.join();
		} catch (InterruptedException e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	/**
	 * 存储某一消息的所有分包,键值Key是车辆的SimNo，Value是某一消息包的所有分包 如果消息有分包，则当收到所有分包后，才能进行完整的解析
	 */
	public void processMultiPacketMsg(T808Message msg) {
		T808MessageHeader header = msg.getHeader();
		int packetNo = header.getMessagePacketNo();
		String key = msg.getSimNo() + "_" + msg.getMessageType() + "_"
				+ header.getMessageTotalPacketsCount();
		MultiPacketMessage mp = this.multiPacketMessageMap.get(key);
		if (mp == null) {
			mp = new MultiPacketMessage(msg);
			mp.setKey(key);
			multiPacketMessageMap.put(key, mp);
		} else if (mp.containPacket(packetNo) == false) {
			mp.addPacket(packetNo, msg.getChildPacket());
		}
	}
	
	/**
	 * 线程函数，不断的检测数据包是否全部收到
	 */
	private void checkPacketThreadFunc() {
		while (continueCheck) {
			Collection<MultiPacketMessage> mpList = multiPacketMessageMap.values();
			for (MultiPacketMessage mp : mpList) {
				double seconds = DateUtil.getSeconds(mp.getUpdateDate(),
						new Date());
				if (seconds > mediaUploadTimeOut) {
					if (mp.getRetransCount() > this.maxRetransTimes) {// 已经超过重传次数
						multiPacketMessageMap.remove(mp.getKey());
						logger.error(mp.toString() + "收包失败，超过最大重传次数:" + mp.getRetransCount());
					} else {
						// 对终端要求重传
						mp.setRetransCount(mp.getRetransCount() + 1);
						sendAck(mp);
					}
				}
				if (mp.isComplete()) {
					// 所有包都收到，从内存中移除，保存到文件中
					multiPacketMessageMap.remove(mp.getKey());
					//数据处理服务，当收到完整的包后，重新解析后交给gpsDataService处理
					T808Message tm = mp.getWholeMessage();
					this.gpsDataService.ProcessMessage(tm);
				}
			}

			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	/**
	 * 如果需要重传包，则需要对终端发送0x8003重传分包命令
	 * 
	 * @param mp
	 */
	private void sendAck(MultiPacketMessage mp) {
		ArrayList<Short> packetNos = mp.getNeedReTransPacketNo();
		if(packetNos.size() > 0)
		{
			JT_8003 echoData = new JT_8003();
			echoData.setResponseMessageSerialNo(mp.getMessageSerialNo());
			echoData.setRepassPacketsCount((byte) packetNos.size());
			echoData.setRepassPacketsNo(packetNos);

			T808Message ts = new T808Message();
			ts.setMessageContents(echoData);
			ts.setHeader(new T808MessageHeader());
			ts.getHeader().setMessageType(0x8003);
			ts.getHeader().setSimId(mp.getSimNo());
			ts.getHeader().setIsPackage(false);

			this.messageSender.Send808Message(ts);
			logger.error("对终端下重传包:" + mp.toString());
		}

	}

	public IMessageSender getMessageSender() {
		return messageSender;
	}

	public void setMessageSender(IMessageSender messageSender) {
		this.messageSender = messageSender;
	}

	
	

}
